"""

Adder

"""

# Python Modules
import sys

# Adder Modules
import Bite
import Builtins
from ExpressionParser import *

class Adder():
	def __init__(self):
		self.engine = Bite.Bite();
		
		self.parser = ExpressionParser(self);
		
		self.filename = "<string>"
	
	def interpretCode(self, code, progress = None):
		# This function interprets the code given to it, line-by-line, converting
		# it into Adder and a structure defining classes and functions (these are returned
		# as a tuple).
		self.interpreter_indent_level = 0
		
		code = code.split("\n")
		bytecode = ""
		lineno = 1
		self.parser.current_file = self.filename
		
		# Convert the bytecode.
		
		a = 0
		for i in code:
			self.parser.current_line = lineno
			if (progress != None):
				progress(a, len(code))
			
			b = self.parser.parseExpression(i)
			if (not b.endswith("\n") and b.strip() != ""): b += "\n"
			bytecode += b
			a += 1
			lineno += 1
		b = self.parser.finalizeIndentations()
		if (not b.endswith("\n") and b.strip() != ""): b += "\n"
		bytecode += b
		
		# Now we need to get the classes and functions that have
		# been defined, copy them and then reset them in the engine
		# (in case the user doesn't actually want them defined).
		
		self.engine.unloadBuiltins();
		c = dict(self.engine.classes);
		f = dict(self.engine.functions);
		i = list(self.engine.imports);
		self.engine.classes = dict();
		self.engine.functions = dict();
		self.engine.imports = list();
		self.engine.reloadBuiltins();
		
		# Return bytecode, functions and classes (in that order).
		return bytecode, f, c, i
	
	def loadFunctions(self, functions):
		# Overwrites the function dictionary of the engine with the
		# specified dictionary.
		self.engine.functions = functions
		self.engine.reloadBuiltins();
	
	def loadClasses(self, classes):
		# Overwrites the function dictionary of the engine with the
		# specified dictionary.
		self.engine.classes = classes
		self.engine.reloadBuiltins();
	
	def loadImports(self, imports):
		# Overwrites the function dictionary of the engine with the
		# specified dictionary.
		self.engine.imports = imports
	
	def getBytecode(self, bytecode, functions, classes, imports):
		# Gets the textual representation of the bytecode, functions
		# and classes so that they can be written to file as a .Bite
		# file.
		
		# NOTE: Only functions and constructors of classes are saved
		#       in .Bite files.  Any defined variables will be discarded.
		text = ""
		
		for i in imports:
			text += """IMPORT """ + i + """
"""
		
		for f in functions:
			if (functions[f].code == "CLASS CONSTRUCTOR"):
				# Exclude class constructors.
				continue
			
			args = ""
			for a in functions[f].args:
				args += "," + a
			
			text += """SF """ + functions[f].name + args + """
""" + functions[f].code.strip() + """
SEF
"""
		
		for c in classes:
			text += """SCL """ + classes[c].type + """
"""
			if (classes[c].constructor != None):
				text += """SCO
""" + classes[c].constructor.code.strip() + """
"""
			
			for f in classes[c].functions:
				args = ""
				for a in classes[c].functions[f].args:
					args += "," + a
			
				text += """SF """ + classes[c].functions[f].name + args + """
""" + classes[c].functions[f].code.strip() + """
SEF
"""
			text += """SECL
"""
		
		text += """SM
""" + bytecode.strip() + """
SEM"""
		
		return text
	
	def loadBytecode(self, bytecode_split):
		# This function interprets bytecode that contains S* commands and
		# returns the main bytecode, functions and classes dictionarys.
		
		# NOTE: bytecode must be a list!
		
		current_class = None
		current_function = None
		
		classes = dict()
		functions = dict()
		imports = list()
		bytecode_main = ""
		bytecode = ""
		
		a = 1
		for i in bytecode_split:
			if (i.startswith("IMPORT ")):
				# Import.
				imports.append(i[7:].strip())
			elif (i.startswith("SCL ")):
				# Class start.
				if (current_class != None or current_function != None):
					return self.fatalError(a, i, "Class definition must not be inside class or function.");
				current_class = Bite.Obj(i[4:].strip())
				
			elif (i.startswith("SECL")):
				if (current_class == None or current_function != None):
					return self.fatalError(a, i, "End class definition must not be outside class or inside function.");
				classes[current_class.type] = current_class
				functions[current_class.type] = Builtins.NewClassKW(current_class.type, list())
				
				current_class = None
				
			elif (i.startswith("SF ")):
				# Function start.
				if (current_function != None):
					return self.fatalError(a, i, "Function definition must not be inside function.");
				
				s = i[3:].strip().split(",")
				
				current_function = Bite.Func(s[0], s[1:])
				
			elif (i.startswith("SEF")):
				current_function.code = bytecode
				bytecode = ""
				
				if (current_class != None):
					# Add to class
					current_class.functions[current_function.name] = current_function
				else:
					# Add to global
					functions[current_function.name] = current_function
				
				current_function = None
			
			elif (i.startswith("SCO")):
				# Function start.
				if (current_function != None):
					return self.fatalError(a, i, "Constructor definition must not be inside function.");
				if (current_class == None):
					return self.fatalError(a, i, "Constructor definition must be inside class.");
				
				s = i[3:].strip().split(",")
				
				current_function = Bite.Func(s[0], s[1:])
				
			elif (i.startswith("SECO")):
				current_function.code = bytecode
				bytecode = ""
				
				if (current_class != None):
					# Add to class
					current_class.constructor = current_function
				else:
					return self.fatalError(a, i, "Constructor definition must be inside class.");
				
				current_function = None
			
			elif (i.startswith("SM")):
				# Start of main code block.
				if (current_class != None or current_function != None):
					return self.fatalError(a, i, "Main code block must not be inside class or function.");
				
			elif (i.startswith("SEM")):
				# End of main code block.
				if (current_class != None or current_function != None):
					return self.fatalError(a, i, "Main code block must not be inside class or function.");
			
				bytecode_main = bytecode
				bytecode = ""
			
			else:
				bytecode += i + "\n"
			a += 1
		
		return bytecode_main, functions, classes, imports
	
	def fatalError(self, lineno, code_text, error_text):
		print """Traceback (most recent call last):
  File """ + '"' + self.filename + '"' + """ (bytecode), line """ + str(lineno) + """
    """ + code_text + """

BytecodeError: """ + error_text + """
"""
		return False
	
	def executeBytecode(self, bytecode, functions = None, classes = None, imports = None):
		# This function executes the bytecode through the
		# Bite engine.
		if (functions != None):
			self.loadFunctions(functions);
		if (classes != None):
			self.loadClasses(classes);
		if (imports != None):
			self.loadImports(imports);
		
		self.engine.runByteCode(bytecode.split("\n"))